#define JPEG_INTERNALS
#include "jinclude.h"
#include "jpeglib.h"

/* Private state */

typedef struct {
  struct jpeg_input_controller pub; /* public fields */

  wxjpeg_boolean inheaders;		/* TRUE until first SOS is reached */
} my_input_controller;

typedef my_input_controller * my_inputctl_ptr;


/* Forward declarations */
METHODDEF( int ) consume_markers JPP( ( j_decompress_ptr cinfo ) );

void initial_setup( j_decompress_ptr cinfo ) {
  int ci;
  jpeg_component_info *compptr;
  /* Make sure image isn't bigger than I can handle */
  if( ( long ) cinfo->image_height > ( long ) JPEG_MAX_DIMENSION ||
      ( long ) cinfo->image_width > ( long ) JPEG_MAX_DIMENSION )
  { ERREXIT1( cinfo, JERR_IMAGE_TOO_BIG, ( unsigned int ) JPEG_MAX_DIMENSION ); }
  /* For now, precision must match compiled-in value... */
  if( cinfo->data_precision != BITS_IN_JSAMPLE )
  { ERREXIT1( cinfo, JERR_BAD_PRECISION, cinfo->data_precision ); }
  /* Check that number of components won't exceed internal array sizes */
  if( cinfo->num_components > MAX_COMPONENTS )
    ERREXIT2( cinfo, JERR_COMPONENT_COUNT, cinfo->num_components,
              MAX_COMPONENTS );
  /* Compute maximum sampling factors; check factor validity */
  cinfo->max_h_samp_factor = 1;
  cinfo->max_v_samp_factor = 1;
  for( ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components;
       ci++, compptr++ ) {
    if( compptr->h_samp_factor <= 0 || compptr->h_samp_factor > MAX_SAMP_FACTOR ||
        compptr->v_samp_factor <= 0 || compptr->v_samp_factor > MAX_SAMP_FACTOR )
    { ERREXIT( cinfo, JERR_BAD_SAMPLING ); }
    cinfo->max_h_samp_factor = MAX( cinfo->max_h_samp_factor,
                                    compptr->h_samp_factor );
    cinfo->max_v_samp_factor = MAX( cinfo->max_v_samp_factor,
                                    compptr->v_samp_factor );
  }
  /* We initialize DCT_scaled_size and min_DCT_scaled_size to DCTSIZE.
     In the full decompressor, this will be overridden by jdmaster.c;
     but in the transcoder, jdmaster.c is not used, so we must do it here.
  */
  cinfo->min_DCT_scaled_size = DCTSIZE;
  /* Compute dimensions of components */
  for( ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components;
       ci++, compptr++ ) {
    compptr->DCT_scaled_size = DCTSIZE;
    /* Size in DCT blocks */
    compptr->width_in_blocks = ( JDIMENSION )
                               jdiv_round_up( ( long ) cinfo->image_width * ( long ) compptr->h_samp_factor,
                                   ( long )( cinfo->max_h_samp_factor * DCTSIZE ) );
    compptr->height_in_blocks = ( JDIMENSION )
                                jdiv_round_up( ( long ) cinfo->image_height * ( long ) compptr->v_samp_factor,
                                    ( long )( cinfo->max_v_samp_factor * DCTSIZE ) );
    compptr->downsampled_width = ( JDIMENSION )
                                 jdiv_round_up( ( long ) cinfo->image_width * ( long ) compptr->h_samp_factor,
                                     ( long ) cinfo->max_h_samp_factor );
    compptr->downsampled_height = ( JDIMENSION )
                                  jdiv_round_up( ( long ) cinfo->image_height * ( long ) compptr->v_samp_factor,
                                      ( long ) cinfo->max_v_samp_factor );
    /* Mark component needed, until color conversion says otherwise */
    compptr->component_needed = TRUE;
    /* Mark no quantization table yet saved for component */
    compptr->quant_table = NULL;
  }
  /* Compute number of fully interleaved MCU rows. */
  cinfo->total_iMCU_rows = ( JDIMENSION )
                           jdiv_round_up( ( long ) cinfo->image_height,
                                          ( long )( cinfo->max_v_samp_factor * DCTSIZE ) );
  /* Decide whether file contains multiple scans */
  if( cinfo->comps_in_scan < cinfo->num_components || cinfo->progressive_mode )
  { cinfo->inputctl->has_multiple_scans = TRUE; }
  else
  { cinfo->inputctl->has_multiple_scans = FALSE; }
}


void per_scan_setup( j_decompress_ptr cinfo )
/* Do computations that are needed before processing a JPEG scan */
/* cinfo->comps_in_scan and cinfo->cur_comp_info[] were set from SOS marker */
{
  int ci, mcublks, tmp;
  jpeg_component_info *compptr;
  if( cinfo->comps_in_scan == 1 ) {
    /* Noninterleaved (single-component) scan */
    compptr = cinfo->cur_comp_info[0];
    /* Overall image size in MCUs */
    cinfo->MCUs_per_row = compptr->width_in_blocks;
    cinfo->MCU_rows_in_scan = compptr->height_in_blocks;
    /* For noninterleaved scan, always one block per MCU */
    compptr->MCU_width = 1;
    compptr->MCU_height = 1;
    compptr->MCU_blocks = 1;
    compptr->MCU_sample_width = compptr->DCT_scaled_size;
    compptr->last_col_width = 1;
    /* For noninterleaved scans, it is convenient to define last_row_height
       as the number of block rows present in the last iMCU row.
    */
    tmp = ( int )( compptr->height_in_blocks % compptr->v_samp_factor );
    if( tmp == 0 ) {
      tmp = compptr->v_samp_factor;
    }
    compptr->last_row_height = tmp;
    /* Prepare array describing MCU composition */
    cinfo->blocks_in_MCU = 1;
    cinfo->MCU_membership[0] = 0;
  } else {
    /* Interleaved (multi-component) scan */
    if( cinfo->comps_in_scan <= 0 || cinfo->comps_in_scan > MAX_COMPS_IN_SCAN )
      ERREXIT2( cinfo, JERR_COMPONENT_COUNT, cinfo->comps_in_scan,
                MAX_COMPS_IN_SCAN );
    /* Overall image size in MCUs */
    cinfo->MCUs_per_row = ( JDIMENSION )
                          jdiv_round_up( ( long ) cinfo->image_width,
                                         ( long )( cinfo->max_h_samp_factor * DCTSIZE ) );
    cinfo->MCU_rows_in_scan = ( JDIMENSION )
                              jdiv_round_up( ( long ) cinfo->image_height,
                                  ( long )( cinfo->max_v_samp_factor * DCTSIZE ) );
    cinfo->blocks_in_MCU = 0;
    for( ci = 0; ci < cinfo->comps_in_scan; ci++ ) {
      compptr = cinfo->cur_comp_info[ci];
      /* Sampling factors give # of blocks of component in each MCU */
      compptr->MCU_width = compptr->h_samp_factor;
      compptr->MCU_height = compptr->v_samp_factor;
      compptr->MCU_blocks = compptr->MCU_width * compptr->MCU_height;
      compptr->MCU_sample_width = compptr->MCU_width * compptr->DCT_scaled_size;
      /* Figure number of non-dummy blocks in last MCU column & row */
      tmp = ( int )( compptr->width_in_blocks % compptr->MCU_width );
      if( tmp == 0 ) {
        tmp = compptr->MCU_width;
      }
      compptr->last_col_width = tmp;
      tmp = ( int )( compptr->height_in_blocks % compptr->MCU_height );
      if( tmp == 0 ) {
        tmp = compptr->MCU_height;
      }
      compptr->last_row_height = tmp;
      /* Prepare array describing MCU composition */
      mcublks = compptr->MCU_blocks;
      if( cinfo->blocks_in_MCU + mcublks > D_MAX_BLOCKS_IN_MCU )
      { ERREXIT( cinfo, JERR_BAD_MCU_SIZE ); }
      while( mcublks-- > 0 ) {
        cinfo->MCU_membership[cinfo->blocks_in_MCU++] = ci;
      }
    }
  }
}

void latch_quant_tables( j_decompress_ptr cinfo ) {
  int ci, qtblno;
  jpeg_component_info *compptr;
  JQUANT_TBL * qtbl;
  for( ci = 0; ci < cinfo->comps_in_scan; ci++ ) {
    compptr = cinfo->cur_comp_info[ci];
    /* No work if we already saved Q-table for this component */
    if( compptr->quant_table != NULL )
    { continue; }
    /* Make sure specified quantization table is present */
    qtblno = compptr->quant_tbl_no;
    if( qtblno < 0 || qtblno >= NUM_QUANT_TBLS ||
        cinfo->quant_tbl_ptrs[qtblno] == NULL )
    { ERREXIT1( cinfo, JERR_NO_QUANT_TABLE, qtblno ); }
    /* OK, save away the quantization table */
    qtbl = ( JQUANT_TBL * )
           ( *cinfo->mem->alloc_small )( ( j_common_ptr ) cinfo, JPOOL_IMAGE,
                                         SIZEOF( JQUANT_TBL ) );
    MEMCOPY( qtbl, cinfo->quant_tbl_ptrs[qtblno], SIZEOF( JQUANT_TBL ) );
    compptr->quant_table = qtbl;
  }
}

METHODDEF( void )
start_input_pass( j_decompress_ptr cinfo ) {
  per_scan_setup( cinfo );
  latch_quant_tables( cinfo );
  ( *cinfo->entropy->start_pass )( cinfo );
  #if defined(__VISAGECPP__)
  ( *cinfo->coef->start_input_pass2 )( cinfo );
  #else
  ( *cinfo->coef->start_input_pass )( cinfo );
  #endif
  cinfo->inputctl->consume_input = cinfo->coef->consume_data;
}


/*
   Finish up after inputting a compressed-data scan.
   This is called by the coefficient controller after it's read all
   the expected data of the scan.
*/

METHODDEF( void )
finish_input_pass( j_decompress_ptr cinfo ) {
  cinfo->inputctl->consume_input = consume_markers;
}


/*
   Read JPEG markers before, between, or after compressed-data scans.
   Change state as necessary when a new scan is reached.
   Return value is JPEG_SUSPENDED, JPEG_REACHED_SOS, or JPEG_REACHED_EOI.

   The consume_input method pointer points either here or to the
   coefficient controller's consume_data routine, depending on whether
   we are reading a compressed data segment or inter-segment markers.
*/

METHODDEF( int )
consume_markers( j_decompress_ptr cinfo ) {
  my_inputctl_ptr inputctl = ( my_inputctl_ptr ) cinfo->inputctl;
  int val;
  if( inputctl->pub.eoi_reached ) /* After hitting EOI, read no further */
  { return JPEG_REACHED_EOI; }
  val = ( *cinfo->marker->read_markers )( cinfo );
  switch( val ) {
    case JPEG_REACHED_SOS:	/* Found SOS */
      if( inputctl->inheaders ) {	/* 1st SOS */
        initial_setup( cinfo );
        inputctl->inheaders = FALSE;
        /* Note: start_input_pass must be called by jdmaster.c
           before any more input can be consumed.  jdapimin.c is
           responsible for enforcing this sequencing.
        */
      } else {			/* 2nd or later SOS marker */
        if( ! inputctl->pub.has_multiple_scans )
        { ERREXIT( cinfo, JERR_EOI_EXPECTED ); } /* Oops, I wasn't expecting this! */
        start_input_pass( cinfo );
      }
      break;
    case JPEG_REACHED_EOI:	/* Found EOI */
      inputctl->pub.eoi_reached = TRUE;
      if( inputctl->inheaders ) {	/* Tables-only datastream, apparently */
        if( cinfo->marker->saw_SOF )
        { ERREXIT( cinfo, JERR_SOF_NO_SOS ); }
      } else {
        /* Prevent infinite loop in coef ctlr's decompress_data routine
           if user set output_scan_number larger than number of scans.
        */
        if( cinfo->output_scan_number > cinfo->input_scan_number )
        { cinfo->output_scan_number = cinfo->input_scan_number; }
      }
      break;
    case JPEG_SUSPENDED:
      break;
  }
  return val;
}


/*
   Reset state to begin a fresh datastream.
*/

METHODDEF( void )
reset_input_controller( j_decompress_ptr cinfo ) {
  my_inputctl_ptr inputctl = ( my_inputctl_ptr ) cinfo->inputctl;
  inputctl->pub.consume_input = consume_markers;
  inputctl->pub.has_multiple_scans = FALSE; /* "unknown" would be better */
  inputctl->pub.eoi_reached = FALSE;
  inputctl->inheaders = TRUE;
  /* Reset other modules */
  ( *cinfo->err->reset_error_mgr )( ( j_common_ptr ) cinfo );
  ( *cinfo->marker->reset_marker_reader )( cinfo );
  /* Reset progression state -- would be cleaner if entropy decoder did this */
  cinfo->coef_bits = NULL;
}

void jinit_input_controller( j_decompress_ptr cinfo ) {
  my_inputctl_ptr inputctl;
  /* Create subobject in permanent pool */
  inputctl = ( my_inputctl_ptr )
             ( *cinfo->mem->alloc_small )( ( j_common_ptr ) cinfo, JPOOL_PERMANENT,
                 SIZEOF( my_input_controller ) );
  cinfo->inputctl = ( struct jpeg_input_controller * ) inputctl;
  /* Initialize method pointers */
  inputctl->pub.consume_input = consume_markers;
  inputctl->pub.reset_input_controller = reset_input_controller;
  inputctl->pub.start_input_pass = start_input_pass;
  inputctl->pub.finish_input_pass = finish_input_pass;
  /* Initialize state: can't use reset_input_controller since we don't
     want to try to reset other modules yet.
  */
  inputctl->pub.has_multiple_scans = FALSE; /* "unknown" would be better */
  inputctl->pub.eoi_reached = FALSE;
  inputctl->inheaders = TRUE;
}

